package com.tools.common.io;

import com.tools.common.container.r.OnlyReadHashMap;
import com.tools.common.exception.DataGenerationException;
import com.tools.common.exception.IORuntimeException;
import com.tools.common.object.C;
import com.tools.common.object.Note;
import com.tools.common.object.ObjKit;
import com.tools.common.thread.ThreadPoolSource;
import com.tools.common.thread.TimePair;
import com.tools.common.thread.Timeunit;
import com.tools.db.jdbc.DBSource;
import com.tools.middle.elasticsearch.ESKit;
import com.tools.middle.elasticsearch.ESSource;
import com.tools.middle.redis.RedisSource;
import com.tools.web.ali.AlipaySource;
import com.tools.web.email.MailProtocolEnum;
import com.tools.web.email.MailSource;
import com.tools.web.filter.cors.CorsSource;
import com.tools.web.filter.gateway.GetawaySource;
import com.tools.web.filter.gateway.IPBlacksThreshold;
import com.tools.web.interceptor.access.AccessSource;
import com.tools.web.interceptor.auth.AuthSource;
import com.tools.web.interceptor.auth.TokenExtender;
import com.tools.web.interceptor.limiting.RequestLimitSource;
import com.tools.web.interceptor.quota.QuotaSource;
import com.tools.web.sms.NetSms;
import com.tools.web.work.obs.OBSSource;
import com.tools.web.wx.WxSource;
import org.apache.http.HttpHost;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 读取项目配置文件工具类
 * */
@Note("读取项目配置文件工具类")
public final class ProfileKit {

    @Note("配置项容器")
    private static Map<String, Object> CONFIG;

    @Note("是否扫描到 SpringBoot 核心配置文件 application")
    private static boolean HAS_APP;

    static {
        Logger logger = LoggerFactory.getLogger(ProfileKit.class);
        Map<String, Object> temp = new HashMap<>();
        private_readCustomConfig(logger, temp);
        logger.info("------------------------------------------------------------------");
        private_readApplication(logger, temp);
        CONFIG = new OnlyReadHashMap<>(temp);
    }

    /* ************************************************************************************************************
     *
     *      公共方法
     *
     * ************************************************************************************************************
     * */

    @Note("是否扫描到 SpringBoot 核心配置文件 application")
    public static boolean hasApplication() {
        return HAS_APP;
    }


    @Note("移除配置文件集合的引用。【请在 SpringBoot 初始化完成后不再使用时再执行】")
    public static void clear() {
        if(CONFIG == null) return;
        synchronized (ProfileKit.class) {
            if(CONFIG != null) {
                CONFIG = null;
            }
        }
    }

    @Note("获取配置项的容器，注意该容器是只读容器，不能增删改配置项")
    public static Map<String, Object> copyProfileConfigMap() {
        return (CONFIG == null || CONFIG.isEmpty())
                ? new OnlyReadHashMap<>()
                : new OnlyReadHashMap<>(CONFIG);
    }

    @Note("根据配置项的名称获取配置项的内容")
    public static boolean contains(String name) {
        if(CONFIG == null || CONFIG.isEmpty()) return false;
        return CONFIG.containsKey(name);
    }

    @Note("根据配置项的名称获取配置项的内容")
    public static Object get(String name) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        return CONFIG.get(name);
    }

    @Note("根据配置项的名称获取配置项的内容，并把内容强转为目标类型（若类型不兼容则报错）")
    public static <T> T get(String name, Class<T> castType) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        return (T) CONFIG.get(name);
    }


    @Note("根据配置项的名称获取配置项的内容，并把内容尽可能地转化为目标类型")
    public static <T> T get(String name, C<T> bc) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        Object o = CONFIG.get(name);
        return (T) ObjKit.toRealValue(o, bc);
    }


    @Note("根据配置项的名称获取配置项的内容，并把内容转为字符串返回")
    public static String getString(String name) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        Object o = CONFIG.get(name);
        return (o == null) ? null : o.toString();
    }

    @Note("获取字符串类型的配置项参数返回，若参数为 null 则返回默认值")
    public static String getString(String name, String defaultValue) {
        String value = getString(name);
        return (value == null) ? defaultValue : value;
    }

    @Note("根据配置项的名称获取配置项的内容，并把内容转为 Integer 类型的整数返回")
    public static Integer getInteger(String name) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        Object o = CONFIG.get(name);
        if(o == null) return null;
        if(o instanceof Double) return ((Double) o).intValue();
        if(o instanceof Long) return ((Long) o).intValue();
        return (Integer) o;
    }

    @Note("获取 Integer 类型的配置项参数返回，若参数为 null 则返回默认值")
    public static Integer getInteger(String name, int defaultValue) {
        Integer value = getInteger(name);
        return (value == null) ? defaultValue : value;
    }

    @Note("根据配置项的名称获取配置项的内容，并把内容转为 Integer 类型的整数返回")
    public static Long getLong(String name) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        Object o = CONFIG.get(name);
        if(o == null) return null;
        if(o instanceof Integer) return ((Integer) o).longValue();
        if(o instanceof Double) return ((Double) o).longValue();
        return (Long) o;
    }

    @Note("获取 Long 类型的配置项参数返回，若参数为 null 则返回默认值")
    public static Long getLong(String name, long defaultValue) {
        Long value = getLong(name);
        return (value == null) ? defaultValue : value;
    }

    @Note("根据配置项的名称获取配置项的内容，并把内容转为 Integer 类型的整数返回")
    public static Double getDouble(String name) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        Object o = CONFIG.get(name);
        if(o == null) return null;
        if(o instanceof Integer) return ((Integer) o).doubleValue();
        if(o instanceof Long) return ((Long) o).doubleValue();
        return (Double) o;
    }

    @Note("获取 Double 类型的配置项参数返回，若参数为 null 则返回默认值")
    public static Double getDouble(String name, double defaultValue) {
        Double value = getDouble(name);
        return (value == null) ? defaultValue : value;
    }


    @Note("根据配置项的名称获取配置项的内容，并把内容转为 Integer 类型的整数返回")
    public static Float getFloat(String name) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        Object o = CONFIG.get(name);
        if(o == null) return null;
        if(o instanceof Integer) return ((Integer) o).floatValue();
        if(o instanceof Long) return ((Long) o).floatValue();
        if(o instanceof Double) return ((Double) o).floatValue();
        return (Float) o;
    }

    @Note("获取 Float 类型的配置项参数返回，若参数为 null 则返回默认值")
    public static Float getFloat(String name, float defaultValue) {
        Float value = getFloat(name);
        return (value == null) ? defaultValue : value;
    }

    @Note("根据配置项的名称获取配置项的内容，并把内容转为 Integer 类型的整数返回")
    public static Boolean getBoolean(String name) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        Object o = CONFIG.get(name);
        if(o == null) return null;
        return (Boolean) o;
    }

    @Note("获取 Float 类型的配置项参数返回，若参数为 null 则返回默认值")
    public static Boolean getBoolean(String name, boolean defaultValue) {
        Boolean value = getBoolean(name);
        return (value == null) ? defaultValue : value;
    }

    @Note("根据配置项的名称获取配置项的内容，并把内容转为时间单位枚举实例返回")
    public static Timeunit getTimeunit(String name) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        String unit = getString(name);
        return Timeunit.getEnumSafeByUnit(unit);
    }

    @Note("获取时间单位枚举实例返回，若实例为 null 则返回默认值")
    public static Timeunit getTimeunit(String name, Timeunit defaultValue) {
        Timeunit timeunit = getTimeunit(name);
        return (timeunit == null) ? defaultValue : timeunit;
    }

    @Note("获取时间配置项和其单位的，并封装为 TimePair 实例返回")
    public static TimePair getTimePair(String timeName, String timeunitName) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        Long time = getLong(timeName);
        if(time == null || time < 0) return null;
        Timeunit timeunit = getTimeunit(timeunitName);
        if(timeunit == null) return null;
        return new TimePair(time, timeunit);
    }

    @Note("获取配置文件下配置的文件的绝对路径")
    public static String getResourcePath(String name) {
        if(CONFIG == null || CONFIG.isEmpty()) return "";
        String path = (String) CONFIG.get(name);
        return FileKit.getResourceFilePath(path);
    }

    @Note("获取配置文件下配置的文件的字节流")
    public static InputStream getResourceAsStream(String name) {
        if(CONFIG == null || CONFIG.isEmpty()) return null;
        String path = (String) CONFIG.get(name);
        return FileKit.getResourceInputStream(path);
    }


    @Note("获取容器中配置项的个数")
    public static int size() {
        if(CONFIG == null || CONFIG.isEmpty()) return 0;
        return CONFIG.size();
    }


    @Note("把容器转为一个新的 Properties 对象返回")
    public static Properties toProperties() {
        return ObjKit.toProperties(CONFIG);
    }

    /* ************************************************************************************************************
     *
     *      获取配置项，并将其转为对应配置实例的方法
     *
     * ************************************************************************************************************
     * */

    @Note("把配置文件中关于自定义的线程池的所有配置项扫描出来，然后封装成一个 ThreadPoolSource 包装类对象返回")
    public static ThreadPoolSource getThreadPoolSource() {
        if(CONFIG != null && !CONFIG.isEmpty()) {
            ThreadPoolSource poolSource = new ThreadPoolSource();
            poolSource.setCoreSize((Integer) CONFIG.get("thread-pool-core-size"));
            poolSource.setMaxSize((Integer) CONFIG.get("thread-pool-max-size"));
            poolSource.setQueueCapacity((Integer) CONFIG.get("thread-pool-queue-capacity"));
            poolSource.setThreadPrefix((String) CONFIG.get("thread-pool-name-prefix"));
            poolSource.setKeepAliveTime(getLong("thread-pool-keep-alive-time"));
            String timeunitText = (String) CONFIG.get("thread-pool-keep-alive-time-unit");
            Timeunit timeunit = Timeunit.getEnumSafeByUnit(timeunitText);
            poolSource.setKeepAliveTimeunit(timeunit == null ? null : timeunit.asTimeUnit());
            return poolSource;
        }
        return null;
    }


    @Note("把配置文件中关于数据库的配置项扫描出来，然后封装成一个 DBSource 包装类对象返回")
    public static DBSource getDataBaseSource() {
        return HAS_APP
                ? getSpringDataBaseSource()
                : getOwnDataBaseSource();
    }


    @Note("获取 Spring 配置文件配置的连接池参数")
    public static DBSource getSpringDataBaseSource() {
        DBSource dbSource = new DBSource();
        if(CONFIG == null || CONFIG.isEmpty()) return dbSource;
        dbSource.setDriverClass((String) CONFIG.get("spring.datasource.driver-class-name"));
        dbSource.setUrl((String) CONFIG.get("spring.datasource.url"));
        dbSource.setUsername((String) CONFIG.get("spring.datasource.username"));
        dbSource.setPassword((String) CONFIG.get("spring.datasource.password"));
        dbSource.setInitialSize((Integer) CONFIG.get("spring.datasource.druid.initial-size"));
        dbSource.setMaxActive((Integer) CONFIG.get("spring.datasource.druid.max-active"));
        dbSource.setMinIdle((Integer) CONFIG.get("spring.datasource.druid.min-idle"));
        dbSource.setMaxWait((Integer) CONFIG.get("spring.datasource.druid.max-wait"));
        return dbSource;
    }


    @Note("获取自定义配置文件 【custom-config】 配置的连接池参数")
    public static DBSource getOwnDataBaseSource() {
        DBSource dbSource = new DBSource();
        if(CONFIG == null || CONFIG.isEmpty()) return dbSource;
        dbSource.setDriverClass((String) CONFIG.get("db-driver"));
        dbSource.setUrl((String) CONFIG.get("db-url"));
        dbSource.setUsername((String) CONFIG.get("db-username"));
        dbSource.setPassword((String) CONFIG.get("db-password"));
        dbSource.setInitialSize((Integer) CONFIG.get("db-initial-size"));
        dbSource.setMaxActive((Integer) CONFIG.get("db-max-active"));
        dbSource.setMinIdle((Integer) CONFIG.get("db-min-idle"));
        dbSource.setMaxWait((Integer) CONFIG.get("db-max-wait"));
        return dbSource;
    }


    @Note("把配置文件中关于自定义的邮箱的所有配置项扫描出来，然后封装成一个 MailSource 包装类对象返回")
    public static MailSource getMailSource() {
        MailSource mailSource = new MailSource();
        if(CONFIG != null && !CONFIG.isEmpty()) {
            MailProtocolEnum sendProtocol = MailProtocolEnum.getEnumSafe((String) CONFIG.get("mail-send-protocol"));
            if(sendProtocol != null) mailSource.setSendProtocol(sendProtocol);
            mailSource.setSendServerHost((String) CONFIG.get("mail-send-server-host"));
            mailSource.setSendMailer((String) CONFIG.get("mail-send-mail-address"));
            mailSource.setAuthCode((String) CONFIG.get("mail-auth-code"));
            mailSource.setSendAlias((String) CONFIG.get("mail-send-alias"));
            Boolean isEnableDebug = (Boolean) CONFIG.get("mail-enable-debug");
            if(isEnableDebug != null) mailSource.setEnableDebug(isEnableDebug);
            MailProtocolEnum receiveProtocol = MailProtocolEnum.getEnumSafe((String) CONFIG.get("mail-receive-protocol"));
            if(receiveProtocol != null) mailSource.setReceiveProtocol(receiveProtocol);
            mailSource.setReceiveServerHost((String) CONFIG.get("mail-receive-server-host"));
            mailSource.setReceiveMailer((String) CONFIG.get("mail-receive-mail-address"));
            mailSource.setReceiveAlias((String) CONFIG.get("mail-receive-alias"));
        }
        return mailSource;
    }


    @Note("把配置文件中关于自定义的 Redis 的所有配置项扫描出来，然后封装成一个 RedisSource 包装类对象返回")
    public static RedisSource getRedisSource() {
        RedisSource source = new RedisSource();
        if(CONFIG == null || CONFIG.isEmpty()) return source;
        source.setMaxTotal((Integer) CONFIG.get("redis-jedis-maxTotal"));
        source.setMaxIdle((Integer) CONFIG.get("redis-jedis-maxIdle"));
        source.setMinIdle((Integer) CONFIG.get("redis-jedis-minIdle"));
        source.setMaxWait((Integer) CONFIG.get("redis-jedis-maxWait"));
        source.setConnectionTimeout((Integer) CONFIG.get("redis-jedis-connection-timeout"));
        source.setSocketTimeout((Integer) CONFIG.get("redis-jedis-socket-timeout"));
        source.setDatabaseIndex((Integer) CONFIG.get("redis-jedis-database"));
        source.setPassword((String) CONFIG.get("redis-jedis-password"));
        String hosts = (String) CONFIG.get("redis-jedis-hosts");
        if(hosts == null || hosts.isEmpty()) {
            throw new DataGenerationException("没有扫描到 Redis 节点，请至少配置一个以上的 Redis 节点：【节点ip:节点端口】。" +
                    "多个节点用,分隔：【ip1:端口1,ip2:端口2……】 注意,前后不要有其他字符");
        }
        String[] a = hosts.split(",");
        //单机
        if(a.length < 2) {
            return source.add(a[0]);
        }
        //集群
        return source.addAll(a);
    }

    @Note("把配置文件中关于 OBS 对象存储的配置项扫描出来，然后封装成一个 ObsSource 包装类对象返回")
    public static OBSSource getObsSource() {
        OBSSource source = new OBSSource();
        if(CONFIG == null || CONFIG.isEmpty()) return source;
        source.setEndPoint((String) CONFIG.get("obs-end-point"));
        source.setAccessKey((String) CONFIG.get("obs-access-key"));
        source.setSecretKey((String) CONFIG.get("obs-secret-key"));
        Long uploadSize = getLong("obs-part-size-of-upload");
        if(uploadSize != null) {
            source.setPartSizeOfUpload(uploadSize);
        }
        return source;
    }


    @Note("把配置文件中关支付宝登录的配置项扫描出来，然后封装成一个 AlipaySource 包装类对象返回")
    public static AlipaySource getAlipaySource() {
        AlipaySource source = new AlipaySource();
        if(CONFIG == null || CONFIG.isEmpty()) return source;
        source.setAppId((String) CONFIG.get("alipay-oauth-appid"));
        source.setPublicKey((String) CONFIG.get("alipay-oauth-public-key"));
        source.setPrivateKey((String) CONFIG.get("alipay-oauth-private-key"));
        source.setRedirectUri((String) CONFIG.get("alipay-oauth-callback-url"));
        source.setGetaway((String) CONFIG.get("alipay-oauth-getaway"));
        String s = (String) CONFIG.get("alipay-oauth-charset");
        source.setCharset(CharsetEnum.getEnumSafe(s));
        source.setScope((String) CONFIG.get("alipay-oauth-scope"));
        return source;
    }



    @Note("把配置文件中关于自定义的 ElasticSearch 的所有配置项扫描出来，然后封装成一个 ESSource 包装类对象返回")
    public static ESSource getESSource() {
        ESSource source = new ESSource();
        if(CONFIG == null || CONFIG.isEmpty()) return source;
        source.setUsername((String) CONFIG.get("elasticsearch-username"));
        source.setPassword((String) CONFIG.get("elasticsearch-password"));
        source.setRequestTimeout(getLong("elasticsearch-connection-request-timeout"));
        source.setConnectTimeout(getLong("elasticsearch-connect-timeout"));
        source.setSocketTimeout(getLong("elasticsearch-socket-timeout"));
        source.setMaxConnectTotal((Integer) CONFIG.get("elasticsearch-max-conn-total"));
        source.setMaxConnectPreRoute((Integer) CONFIG.get("elasticsearch-max-conn-per-route"));
        String hosts = (String) CONFIG.get("elasticsearch-nodes");
        if(hosts == null || hosts.isEmpty()) {
            throw new DataGenerationException("没有扫描到 ElasticSearch 节点，请至少配置一个以上的 ElasticSearch 节点：【节点ip:节点端口】。" +
                    "多个节点用,分隔：【ip1:端口1,ip2:端口2……】 注意,前后不要有其他字符");
        }
        String[] a = hosts.split(",");
        int length = a.length;
        //单机
        if(length < 2) {
            HttpHost hostNode = ESKit.newHostNode(a[0]);
            source.setNodes(new HttpHost[] {hostNode});
            return source;
        }
        //集群
        HttpHost[] httpHosts = new HttpHost[length];
        for (int i = 0; i < length; i++) {
            httpHosts[i] = ESKit.newHostNode(a[i]);
        }
        source.setNodes(httpHosts);
        return source;
    }


    @Note("获取自定义过滤器配置项包装对象")
    public static CorsSource getCorsSource() {
        CorsSource source = new CorsSource();
        if(CONFIG == null || CONFIG.isEmpty()) return source;
        source.setAllowOrigin((String) CONFIG.get("cors-filter-allow-origin"));
        source.setAllowMethods((String) CONFIG.get("cors-filter-allow-methods"));
        source.setAllowHeaders((String) CONFIG.get("cors-filter-allow-headers"));
        source.setAllowCredentials((Boolean) CONFIG.get("cors-filter-allow-credentials"));
        source.setMaxAge((Integer) CONFIG.get("cors-filter-max-age"));
        return source;
    }


    @Note("获取自定义网关配置项包装对象")
    public static GetawaySource getGetawaySource() {
        GetawaySource source = new GetawaySource();
        if(CONFIG == null || CONFIG.isEmpty()) return source;
        source.setResponseContentType((String) CONFIG.get("getaway-filter-response-content-type"));
        String charsetString = (String) CONFIG.get("getaway-filter-response-charset");
        source.setResponseCharset(CharsetEnum.getEnumSafe(charsetString));
        source.setProxyWhitesCacheKey((String) CONFIG.get("getaway-filter-proxyWhitesCacheKey"));
        source.setDisableURICacheKey((String) CONFIG.get("getaway-filter-disabledURICacheKey"));
        source.setIpWhitesCacheKey((String) CONFIG.get("getaway-filter-ipWhitesCacheKey"));
        source.setIpBlacksCacheKey((String) CONFIG.get("getaway-filter-ipBlacksCacheKey"));
        source.setProxyIPs((String) CONFIG.get("getaway-filter-init-load-proxyIPs"));
        source.setProxyIPsFilePath((String) CONFIG.get("getaway-filter-init-load-proxyIPs-filepath"));
        source.setDisabledURIs((String) CONFIG.get("getaway-filter-init-load-disabledURIs"));
        source.setDisabledURIsFilePath((String) CONFIG.get("getaway-filter-init-load-disabledURIs-filepath"));
        source.setWhitesIps((String) CONFIG.get("getaway-filter-init-load-whitesIps"));
        source.setWhitesIpsFilePath((String) CONFIG.get("getaway-filter-init-load-whitesIps-filepath"));
        source.setBlacksIps((String) CONFIG.get("getaway-filter-init-load-blacksIps"));
        source.setBlacksIpsFilePath((String) CONFIG.get("getaway-filter-init-load-blacksIps-filepath"));
        //跨域配置
        Boolean b = (Boolean) CONFIG.get("getaway-filter-open-cors-filter");
        if(b != null && b) {
            CorsSource corsSource = getCorsSource();
            source.setCorsSource(corsSource);
        }
        //动态拉黑规则配置
        String thresholdKeyPrefix = (String) CONFIG.get("getaway-filter-black-access-threshold-key-prefix");
        if(thresholdKeyPrefix == null || thresholdKeyPrefix.isEmpty()) return source;
        Integer thresholdCount = (Integer) CONFIG.get("getaway-filter-black-access-threshold");
        if(thresholdCount == null || thresholdCount <= 0) return source;
        Long thresholdTime = getLong("getaway-filter-black-access-threshold-time");
        if(thresholdTime == null || thresholdTime <= 0) return source;
        Timeunit thresholdTimeunit = getTimeunit("getaway-filter-black-access-threshold-timeunit");
        if(thresholdTimeunit == null) return source;
        IPBlacksThreshold threshold = new IPBlacksThreshold(thresholdKeyPrefix, thresholdCount, thresholdTime, thresholdTimeunit);
        Boolean enableDisabledAccount = (Boolean) CONFIG.get("getaway-filter-black-enable-disable-account");
        if(enableDisabledAccount != null) {
            threshold.setUseAuthHandler(enableDisabledAccount);
        }
        source.setIpBlacksThreshold(threshold);
        return source;
    }


    @Note("获取限流拦截器的配置参数包装对象")
    public static RequestLimitSource getRequestLimitSource() {
        RequestLimitSource source = new RequestLimitSource();
        if(CONFIG == null || CONFIG.isEmpty()) return source;
        source.setCachePrefix((String) CONFIG.get("limit-interceptor-keyPrefix"));
        source.setLimitResponseMessage((String) CONFIG.get("limit-interceptor-limitResponseMessage"));
        Boolean useAuthHandler = (Boolean) CONFIG.get("limit-interceptor-useAuthHandler");
        if(useAuthHandler != null) {
            source.setUseAuthHandler(useAuthHandler);
        }
        source.setDisabledResponseMessage((String) CONFIG.get("limit-interceptor-disabledResponseMessage"));
        return source;
    }


    @Note("获取用户认证拦截器的配置参数保证对象")
    public static AuthSource getAuthSource() {
        AuthSource source = new AuthSource();
        if(CONFIG == null || CONFIG.isEmpty()) return source;
        source.setTokenFromHeader((Boolean) CONFIG.get("auth-interceptor-token-from-header"));
        source.setTokenName((String) CONFIG.get("auth-interceptor-token-name"));
        source.setTokenSeparator((String) CONFIG.get("auth-interceptor-token-separator"));
        source.setCacheKeyPrefix((String) CONFIG.get("auth-interceptor-token-cache-key-prefix"));
        source.setTokenNotFoundErrorJson((String) CONFIG.get("auth-interceptor-tokenNotFoundErrorJson"));
        source.setTokenParseFailedErrorJson((String) CONFIG.get("auth-interceptor-tokenParseFailedErrorJson"));
        source.setTokenExpiredJson((String) CONFIG.get("auth-interceptor-tokenExpiredJson"));
        source.setDeviceTypeIdAndMaxTotalJsonString((String) CONFIG.get("auth-interceptor-deviceTypeIdAndMaxTotal"));
        source.setCypher(AuthSource.buildCypherByConfig(CONFIG));
        //在认证管理器里开启授权功能
        String authorizeTypeClassName = (String) CONFIG.get("auth-interceptor-enable-authorize-type");
        if(authorizeTypeClassName != null && !authorizeTypeClassName.isEmpty()) {
            source.setAccessManagerClassName(authorizeTypeClassName);
            source.setAccessSource(getAccessSource());
        }
        //token 续期
        Boolean enableExtend = (Boolean) CONFIG.get("auth-interceptor-enable-extend");
        if(enableExtend == null || !enableExtend) return source;
        Long extendTime = getLong("auth-interceptor-token-extend-time");
        Timeunit extendTimeunit = getTimeunit("auth-interceptor-token-extend-timeunit");
        if(extendTime == null || extendTime < 1 || extendTimeunit == null) return source;
        TokenExtender tokenExtender = new TokenExtender();
        tokenExtender.setEnableExtend(true);
        tokenExtender.setExtendTime(extendTime, extendTimeunit);
        Long maxAllowExtendTime = getLong("auth-interceptor-token-maxAllowExtendTime");
        Timeunit maxAllowExtendTimeunit = getTimeunit("auth-interceptor-token-maxAllowExtendTimeunit");
        if(maxAllowExtendTime != null && maxAllowExtendTime > 0 && maxAllowExtendTimeunit != null) {
            tokenExtender.setMaxAllowExtendTime(maxAllowExtendTime, maxAllowExtendTimeunit);
        }
        Integer maxAllowExtendCount = (Integer) CONFIG.get("auth-interceptor-token-maxAllowExtendCount");
        if(maxAllowExtendCount != null && maxAllowExtendCount > 0) {
            tokenExtender.setMaxAllowCount(maxAllowExtendCount);
        }
        Long minExtendThresholdTime = getLong("auth-interceptor-token-minExtendThresholdTime");
        Timeunit minExtendThresholdTimeunit = getTimeunit("auth-interceptor-token-minExtendThresholdTimeunit");
        if(minExtendThresholdTime != null && minExtendThresholdTime > 0 && minExtendThresholdTimeunit != null) {
            tokenExtender.setMinExtendThresholdTime(minExtendThresholdTime, minExtendThresholdTimeunit);
        }
        source.setTokenExtender(tokenExtender);
        return source;
    }


    @Note("权限验证拦截器所需的参数配置项的包装类实例")
    public static AccessSource getAccessSource() {
        AccessSource source = new AccessSource();
        if(CONFIG == null || CONFIG.isEmpty()) return source;
        source.setAccessFailResponseMessage((String) CONFIG.get("access-interceptor-accessFailError"));
        source.setUnreleasedResponseMessage((String) CONFIG.get("access-interceptor-unreleasedResponseMessage"));
        return source;
    }

    @Note("获取配额限制拦截器所需的参数配置项的包装类实例")
    public static QuotaSource getQuotaSource() {
        QuotaSource source = new QuotaSource();
        if(CONFIG == null || CONFIG.isEmpty()) return source;
        source.setCachePrefix((String) CONFIG.get("quota-interceptor-key-prefix"));
        source.setResponseMessage((String) CONFIG.get("quota-interceptor-responseMessage"));
        source.setNotHasUniqueIDErrorMessage((String) CONFIG.get("quota-interceptor-notHasUniqueIDErrorMessage"));
        return source;
    }


    @Note("获取网建短信通服务实例对象")
    public static NetSms getNetSms() {
        NetSms netSms = new NetSms();
        if(CONFIG == null || CONFIG.isEmpty()) return netSms;
        netSms.setUid((String) CONFIG.get("sms-net-uid"));
        netSms.setKey((String) CONFIG.get("sms-net-key"));
        netSms.setUrlUtf8Service((String) CONFIG.get("sms-net-utf8-url-service"));
        netSms.setUrlGbkService((String) CONFIG.get("sms-net-gbk-url-service"));
        return netSms;
    }

    @Note("获取微信登录所需配置项的包装实例对象")
    public static WxSource getWxSource() {
        WxSource source = new WxSource();
        if(CONFIG == null || CONFIG.isEmpty()) return source;
        source.setAppId((String) CONFIG.get("wx-login-appid"));
        source.setSecret((String) CONFIG.get("wx-login-secret"));
        return source;
    }


    /* ************************************************************************************************************
     *
     *      私有逻辑
     *
     * ************************************************************************************************************
     * */


    @Note("读取 custom-config.properties 配置文件")
    private static void private_readCustomConfig(Logger logger, Map<String, Object> temp) {
        String customConfigPath = FileKit.getResourceFilePath("custom-config.properties");
        private_customConfigDataPutInMap(new File(customConfigPath), logger, temp);
    }

    @Note("读取 custom-config.properties 自定义配置文件及其配置的子文件的数据项，放入到 temp Map 里")
    private static void private_customConfigDataPutInMap(File file, Logger logger, Map<String, Object> temp) {
        String filename = file.getName();
        String parent = file.getParent();
        logger.info("正在扫描 {} 文件，目录路径 {} ...", filename, parent);
        Properties properties = private_readToProperties(file);
        if(properties == null) {
            logger.error("目录 {} 下的 {} 文件不存在!", parent, filename);
            return;
        }
        String additional = properties.getProperty("additional-filename");
        if(additional != null && !additional.isEmpty()) {
            if("custom-config.properties".equals(filename)) {
                String[] a = additional.split(",");
                logger.info("检测到 {} 的子配置文件: {}", filename, Arrays.toString(a));
                String suffix = FileKit.getFileSuffix(filename);
                String classPath = FileKit.getClassPath();
                for (String fn : a) {
                    File f = new File(classPath, fn + suffix);
                    private_customConfigDataPutInMap(f, logger, temp);
                }
                return;
            }
        }
        for (Object o : properties.entrySet()) {
            Map.Entry e = (Map.Entry) o;
            temp.put((String) e.getKey(), ObjKit.stringToRealValue((String) e.getValue(), false));
        }
        logger.info("{} 文件扫描完成，共加载 {} 项!", filename, properties.size());
    }


    @Note("读取 SpringBoot 核心配置文件的所有配置项")
    private static void private_readApplication(Logger logger, Map<String, Object> temp) {
        logger.info("正在检测是否存在 SpringBoot 核心配置文件: [application.properties, application.yml]...");
        String applicationProPath = FileKit.getResourceFilePath("application.properties");
        boolean proIsExists = private_applicationProDataPutInMap(new File(applicationProPath), logger, temp);
        if(proIsExists) {
            HAS_APP = true;
            return;
        }
        String applicationYmlPath = FileKit.getResourceFilePath("application.properties");
        boolean ymlIsExists = private_applicationYmlDataPutInMap(new File(applicationYmlPath), logger, temp);
        if(ymlIsExists) {
            HAS_APP = true;
            return;
        }
        HAS_APP = false;
        logger.error("没有检索到 SpringBoot 核心配置文件: [application.properties, application.yml] !!!");
    }


    @Note("读取 SpringBoot 核心配置文件 【application.properties】 的所有配置项")
    private static boolean private_applicationProDataPutInMap(File file, Logger logger, Map<String, Object> temp) {
        Properties properties = private_readToProperties(file);
        if(properties == null) return false;
        String filename = file.getName();
        if("application.properties".equals(filename)) {
            logger.info("检测到 SpringBoot 核心配置文件: 【application.properties】，正在加载其中的配置项...");
            String actives = properties.getProperty("spring.profiles.active");
            if(actives != null && !actives.isEmpty()) {
                String[] a = actives.split(",");
                logger.info("检测到 application.properties 的子配置文件: {}", Arrays.toString(a));
                for (String fn : a) {
                    File aFile = FileKit.getResourceFile("application-" + fn + ".properties");
                    private_applicationProDataPutInMap(aFile, logger, temp);
                }
            }
        } else {
            logger.info("检测到 SpringBoot 核心子配置文件: 【{}】，正在加载其中的配置项...", filename);
        }
        for (Object o : properties.entrySet()) {
            Map.Entry e = (Map.Entry) o;
            temp.put((String) e.getKey(), ObjKit.stringToRealValue((String) e.getValue(), false));
        }
        logger.info("{} 文件扫描完成，共加载 {} 项!", filename, properties.size());
        return true;
    }


    @Note("读取 SpringBoot 核心配置文件 【application.yml】 的所有配置项")
    private static boolean private_applicationYmlDataPutInMap(File file, Logger logger, Map<String, Object> temp) {
        if(!file.exists()) return false;
        Map<String, Object> ymlMap = FileKit.readYml(file);
        String filename = file.getName();
        if("application.yml".equals(filename)) {
            logger.info("检测到 SpringBoot 核心配置文件: 【application.yml】，正在加载其中的配置项...");
            String actives = (String) ymlMap.get("spring.profiles.active");
            if(actives != null && !actives.isEmpty()) {
                String[] a = actives.split(",");
                logger.info("检测到 application.yml 的子配置文件: {}", Arrays.toString(a));
                for (String fn : a) {
                    File aFile = FileKit.getResourceFile("application-" + fn + ".yml");
                    private_applicationProDataPutInMap(aFile, logger, temp);
                }
            }
        } else {
            logger.info("检测到 SpringBoot 核心子配置文件: 【{}】，正在加载其中的配置项...", filename);
        }
        temp.putAll(ymlMap);
        logger.info("{} 文件扫描完成，共加载 {} 项!", filename, ymlMap.size());
        return true;
    }



    @Note("读取 .properties 文件并返回一个 Properties 对象")
    private static Properties private_readToProperties(File file) {
        if(!file.exists()) {
            return null;
        }
        FileReader reader = null;
        try {
            reader = new FileReader(file);
            Properties properties = new Properties();
            properties.load(reader);
            return properties;
        } catch (IOException e) {
            throw new IORuntimeException(e);
        } finally {
            IOKit.close(reader);
        }
    }
}
